#pragma once

#include "PLObject.h"
#include "PLEnums.h"

#include "PLFileNotFoundException.h"
#include "PLIOException.h"
#include "PLPlanarPolygon3dsOptimization.h"

#include <map>
#include <set>
#include <string>
#include <vector>

class PLBlob;
class PLEdge;

/**
  *	A PLImageInfo object capsules the width and height information of a single image
  */
struct PLImageInfo
{
	PLImageInfo( int _width, int _height ) : width(_width), height(_height) {}
	
	/** Width of the image in pixels */
	int width;
	/** Height of the image in pixels */
	int height;
};

/**
  *	A PLMosaic object is used to find all the used regions in the images, that
  * correspond to a model file. Further, adjacent edges are found. These are edges in the
  * model, where regions, textured by differed images, adjoin each other. After the
  * assignment of the vertex colors, calculation of outline paths and normals one can
  * call the CalculateLevellingFunction() method to do levelling. After that, the PLImageFactory
  * can be used to generate the leveled textures.
  */
class PLMosaic : public PLObject
{
public:
	PLMosaic( const std::string &modelFilePath,
		const PLModelFormat &modelFileFormat,
		const std::string &leveledModelFilePath,
		const PLModelFormat &leveledModelFileFormat,
		const PLFilteringMode &filteringMode = PL_FILTERMODE_NEAREST_NEIGHBOR,
		PLuint outlineVertexNormalLength = 20U,
		const PLImageFormat &imageFormat = PL_IMAGEFORMAT_JPEG ) throw( PLIOException, PLFileNotFoundException );
	virtual ~PLMosaic( void );

	void CalculateLevellingFunction( const PLPlanarPolygon3dsOptimizationOptions *options );

	const std::string &GetBasePath( void ) const;
	const std::map<std::string, std::set<PLBlob*>> &GetMaterialNamesToBlobsMap( void ) const;
	const std::map<std::string, std::string> &GetMaterialNamesToImageNamesMap( void ) const;
	const std::map<std::string, std::string> &GetMaterialNamesToLevelledTextureSuffixesMap( void ) const;
	const std::map<std::string, PLImageInfo> &GetImageNamesToImageInfosMap( void ) const;
	const PLuint &GetIndexOffset( void ) const;
	const std::vector<double*> &GetTextureCoordinates( void ) const;

	bool WriteLeveledModelFile( void ) const;
	
private:

	/** Base path of the underlying model file */
	std::string mBasePath;
	/** Map, that maps image IDs to the corresponding image names */
	std::map<PLuint, std::string> mImageIdsToMaterialNamesMap;
	/** Map, that maps material names to sets with all the pointers to PLBlob objects that correspond to one particular material-image */
	std::map<std::string, std::set<PLBlob*>> mMaterialNamesToBlobsMap;
	/** Map, that maps material names to the corresponding image names */
	std::map<std::string, std::string> mMaterialNamesToImageNamesMap;
	/** Map, that maps material names to the corresponding image IDs */
	std::map<std::string, PLuint> mMaterialNamesToImageIdsMap;
	/** Map, that maps image names (not paths!) to the corresponding PLImageInfo objects */
	std::map<std::string, PLImageInfo> mImageNamesToImageInfosMap;
	/** Map, that maps material names to the suffixes strings of the generated image files */
	std::map<std::string, std::string> mMaterialNamesToLevelledTextureSuffixesMap;
	/** The whole leveled material file stored in a vector, one line per element */
	std::vector<std::string> mLeveledMaterialFileLines;
	/** The whole leveled model file stored in a vector, one line per element */
	std::vector<std::string> mLeveledModelFileLines;
	/** Full path of the leveled model file */
	std::string mLeveledModelFilePath;
	/** File path of the leveled material file */
	std::string mLeveledMaterialFilePath;
	/** File format of the leveled model */
	PLModelFormat mLeveledModelFileFormat;

	/** Offset used for the vertex and texcoord indices. It is 1, when the indices start with 1, 0 otherwise */
	PLuint mIndexOffset;
	/** Length of the normals in pixels. This is also the width of the triangle ring measured along the normal */
	PLuint mOutlineVertexNormalLength;
	/** Number of vertexes in the model */
	PLuint mNumVertexesInModel;

	/** Filtering mode used for the sampling of the color values */
	PLFilteringMode mFilteringMode;
	/** Vector with all UV texture coordinates. The coordinates are stored as pointers do double arrays  */
	std::vector<double*> mTextureCoordinates;

	/** Map, that maps texcoord indices to sets, that contain all texcoord indices, that correspond to one particular texcoord */
	std::map<PLuint, std::set<PLuint>*> mEqualTexcoordIndexesMap;
	/** Map, that maps vertex indices to sets, that contain all vertex indices, that correspond to one particular vertex */
	std::map<PLuint, std::set<PLuint>*> mEqualVertexIndexesMap;

	void AssignOriginalColorValuesToVertexes( void ) throw( PLFileNotFoundException );
	void CalculateBlobOutlinePaths( void );
	void CalculateBlobOutlineTriangleRings( void );
	void CalculateBlobOutlineVertexNormals( void );
	void FindAdjacentEdges( void );
	PLuint GetFirstElementFromEqualTexcoordIndexes( const std::string &indexToken ) const;
	PLuint GetFirstElementFromEqualVertexIndexes( const std::string &indexToken ) const;
	void LoadModelFile( const std::string &modelFilePath,
		const PLModelFormat &modelFileFormat,
		const PLImageFormat &imageFormat ) throw( PLIOException );
};